/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import ts from '../../vendor/ohos-typescript';

export function getSpecifyChild(aNode: ts.Node, kind: ts.SyntaxKind): ts.Node | undefined {
    for (const child of aNode.getChildren()) {
        if (child.kind === kind) {
            return child;
        }
        let result = getSpecifyChild(child, kind);
        if (result) {
            return result;
        }
    }
    return undefined;
}

export const ASSIGNMENT_OPERATORS = new Set([
  ts.SyntaxKind.EqualsToken,
  ts.SyntaxKind.PlusEqualsToken,
  ts.SyntaxKind.MinusEqualsToken,
  ts.SyntaxKind.AsteriskEqualsToken,
  ts.SyntaxKind.SlashEqualsToken
]);

export function findAssignments(node: ts.Node): ts.BinaryExpression[] {
  const result: ts.BinaryExpression[] = [];

  const visit = (n: ts.Node): void => {
    if (ts.isBinaryExpression(n) && ASSIGNMENT_OPERATORS.has(n.operatorToken.kind)) {
      result.push(n);
    }
    ts.forEachChild(n, visit);
  };

  visit(node);
  return result;
}

export function containsNonNullAssertion(node: ts.Node): boolean {
  if (ts.isNonNullExpression(node)) { return true };
  return node.getChildren().some(child => containsNonNullAssertion(child));
}

export function checkNew(aNode: ts.Node): boolean {
    let nameNode: ts.Node | undefined = undefined;
    let lastNode: ts.Node | undefined = undefined;
    let nameFlag = false;
    for (const child of aNode.getChildren()) {
        if (nameFlag) {
            nameNode = child;
            break;
        }
        if (lastNode !== undefined && lastNode.kind === ts.SyntaxKind.CloseParenToken && child.kind === ts.SyntaxKind.ColonToken) {
            nameFlag = true;
        }
        lastNode = child;
    }
    if (!nameNode) {
        return false;
    }
    const parentClassNode = findParentClassOrInterfaceNode(aNode);
    if (!parentClassNode) {
        return false;
    }
    if (ts.isInterfaceDeclaration(parentClassNode)) { // interface C { new(): C; }
        const interfaceName = parentClassNode.name.getText();
        let name = nameNode.getText();
        if (nameNode.getChildren().length > 0) {
            name = nameNode.getChildren()[0].getText();
        }
        if (interfaceName === name) {
            return true;
        }
    } else if (ts.isClassDeclaration(parentClassNode)) { // class C { new(): C; }
        const className = parentClassNode.name?.getText();
        if (className && className === nameNode.getText()) {
            return true;
        }
    }
    return false;
}

export function findParentClassOrInterfaceNode(aNode: ts.Node): ts.Node | undefined {
    let parent = aNode.parent;
    while (parent !== undefined) {
        if (ts.isClassDeclaration(parent) || ts.isInterfaceDeclaration(parent) || ts.isTypeAliasDeclaration(parent)) {
            return parent;
        }

        parent = parent.parent;
    }

    return undefined;
}

export function checkConstructor(aNode: ts.Node): boolean {
    const parentClassNode = findParentClassOrInterfaceNode(aNode);
    if (!parentClassNode) {
        return false;
    }

    if (ts.isInterfaceDeclaration(parentClassNode) || ts.isTypeAliasDeclaration(parentClassNode)) {
        return true;
    }
    return false;
}
